using System;
using System.Net;
using System.Collections.Generic;
using MIS.EN.Exchange.Entities;
using Evolution.Business.Specifications;

namespace MIS.EN.Exchange
{
    /// <summary>
    /// This is the main entry point for making requests to the Exchange Store.
    /// </summary>
    public class ExchangeStore
    {
        private NetworkCredential credentials;
        private Uri userMailBox;
        private bool useFormsBasedAuthentication;
        private bool useIsaServer2006;
        private bool userMailBoxValidated;
        private FolderWorker<Appointment> calendarWorker;
        private FolderWorker<Contact> contactWorker;
        private FolderWorker<Task> taskWorker;

        /// <summary>
        /// Overloaded public constructor.
        /// </summary>
        /// <param name="credentials">The <see cref="System.Net.NetworkCredential"/> security account that will be accessing Exchange.</param>
        public ExchangeStore(NetworkCredential credentials) 
            : this(credentials, null, false)
        {
        }

        /// <summary>
        /// Overloaded public constructor.
        /// </summary>
        /// <param name="credentials">The <see cref="System.Net.NetworkCredential"/> security account that will be accessing Exchange.</param>
        /// <param name="userMailBox">The user's mailbox to connect to.</param>
        public ExchangeStore(NetworkCredential credentials, Uri userMailBox)
            : this(credentials, userMailBox, false)
        {
        }

        /// <summary>
        /// Overloaded public constructor.
        /// </summary>
        /// <param name="credentials">The <see cref="System.Net.NetworkCredential"/> security account that will be accessing Exchange.</param>
        /// <param name="userMailBox">The user's mailbox to connect to.</param>
        /// <param name="useFormsBasedAuthentication">True indicates to connect to an Exchange OWA 
        /// server that is using forms-based authentication.</param>
        public ExchangeStore(NetworkCredential credentials, Uri userMailBox, bool useFormsBasedAuthentication)
            : this(credentials, userMailBox, useFormsBasedAuthentication, false)
        {
        }

        /// <summary>
        /// Overloaded public constructor.
        /// </summary>
        /// <param name="credentials">The <see cref="System.Net.NetworkCredential"/> security account that will be accessing Exchange.</param>
        /// <param name="userMailBox">The user's mailbox to connect to.</param>
        /// <param name="useFormsBasedAuthentication">True indicates to connect to an Exchange OWA 
        /// server that is using forms-based authentication.</param>
        /// <param name="useIsaServer2006">True indicates to connect to an Exchange OWA 
        /// server that is using forms-based authentication in conjunction with Microsoft ISA Server 2006.</param>
        public ExchangeStore(NetworkCredential credentials, Uri userMailBox, bool useFormsBasedAuthentication, bool useIsaServer2006)
        {
            this.credentials = credentials;
            this.userMailBox = userMailBox;
            this.useFormsBasedAuthentication = useFormsBasedAuthentication;
            this.useIsaServer2006 = useIsaServer2006;
            this.Initialize();
        }

        private void Initialize()
        {
            this.ValidateCredentials();
            this.ValidateUserMailBox();
            this.calendarWorker = new CalendarFolderWorker(this.credentials, this.useFormsBasedAuthentication, this.useIsaServer2006);
            this.contactWorker = new ContactFolderWorker(this.credentials, this.useFormsBasedAuthentication, this.useIsaServer2006);
            this.taskWorker = new TaskFolderWorker(this.credentials, this.useFormsBasedAuthentication, this.useIsaServer2006);
        }

        /// <summary>
        /// The user's mailbox to connect to.
        /// </summary>
        public Uri UserMailBox
        {
            get { return this.userMailBox; }
            set { this.userMailBox = value; }
        }

        /// <summary>
        /// True indicates to connect to an Exchange OWA 
        /// server that is using forms-based authentication.
        /// </summary>
        public bool UseFormsBasedAuthentication
        {
            get { return this.useFormsBasedAuthentication; }
            set { this.useFormsBasedAuthentication = value; }
        }

        /// <summary>
        /// True indicates to connect to an Exchange OWA 
        /// server that is using forms-based authentication in conjunction with Microsoft ISA Server 2006.
        /// </summary>
        public bool UseIsaServer2006
        {
            get { return this.useIsaServer2006; }
            set { this.useIsaServer2006 = value; }
        }

        /// <summary>
        /// Saves an <see cref="MIS.EN.Exchange.Entities.Appointment"/> item to a user's mailbox.
        /// </summary>
        /// <param name="appointment">The <see cref="MIS.EN.Exchange.Entities.Appointment"/> to save.</param>
        public void SaveAppointment(Appointment appointment)
        {
            this.CheckUserMailBox();
            this.calendarWorker.Save(appointment, this.userMailBox);
        }

        /// <summary>
        /// Saves a <see cref="MIS.EN.Exchange.Entities.Contact"/> item to a user's mailbox.
        /// </summary>
        /// <param name="contact">The <see cref="MIS.EN.Exchange.Entities.Contact"/> to save.</param>
        public void SaveContact(Contact contact)
        {
            this.CheckUserMailBox();
            this.contactWorker.Save(contact, this.userMailBox);
        }

        /// <summary>
        /// Saves a <see cref="MIS.EN.Exchange.Entities.Task"/> item to a user's mailbox.
        /// </summary>
        /// <param name="task">The <see cref="MIS.EN.Exchange.Entities.Task"/> to save.</param>
        public void SaveTask(Task task)
        {
            this.CheckUserMailBox();
            this.taskWorker.Save(task, this.userMailBox);
        }

        /// <summary>
        /// The fully-qualified <see cref="System.Uri"/> of the item to be deleted.
        /// </summary>
        /// <param name="itemUri">The <see cref="System.Uri"/> of the item to be deleted.</param>
        /// <param name="credentials">The <see cref="System.Net.NetworkCredential"/> security account that will be accessing Exchange.</param>
        /// <param name="useFormsBasedAuthentication">True indicates to connect to an Exchange OWA 
        /// server that is using forms-based authentication.</param>
        /// <param name="useIsaServer2006">True indicates to connect to an Exchange OWA 
        /// server that is using forms-based authentication in conjunction with Microsoft ISA Server 2006.</param>
        public static void Delete(Uri itemUri, NetworkCredential credentials, bool useFormsBasedAuthentication, bool useIsaServer2006)
        {
            FolderWorker<Item>.Delete(itemUri, credentials, useFormsBasedAuthentication, useIsaServer2006);
        }

        /// <summary>
        /// Deletes an <see cref="MIS.EN.Exchange.Entities.Appointment"/> from the Exchange Store.
        /// </summary>
        /// <param name="appointment">The <see cref="MIS.EN.Exchange.Entities.Appointment"/> to be deleted.</param>
        public void DeleteAppointment(Appointment appointment)
        {
            this.calendarWorker.Delete(appointment);
        }

        /// <summary>
        /// Deletes a <see cref="MIS.EN.Exchange.Entities.Contact"/> from the Exchange Store.
        /// </summary>
        /// <param name="contact">The <see cref="MIS.EN.Exchange.Entities.Contact"/> to be deleted.</param>
        public void DeleteContact(Contact contact)
        {
            this.contactWorker.Delete(contact);
        }

        /// <summary>
        /// Deletes a <see cref="MIS.EN.Exchange.Entities.Task"/> from the Exchange Store.
        /// </summary>
        /// <param name="task">The <see cref="MIS.EN.Exchange.Entities.Task"/> to be deleted.</param>
        public void DeleteTask(Task task)
        {
            this.taskWorker.Delete(task);
        }

        /// <summary>
        /// Gets all of the <see cref="MIS.EN.Exchange.Entities.Appointment"/> items for the current user's Mailbox.
        /// </summary>
        /// <returns>A <see cref="System.Collections.Generic.IList&lt;T&gt;"/> of type <see cref="MIS.EN.Exchange.Entities.Appointment"/>.</returns>
        public IList<Appointment> GetAppointments()
        {
            this.CheckUserMailBox();
            return this.calendarWorker.Retrieve(this.userMailBox, false);
        }

        /// <summary>
        /// Gets all of the <see cref="MIS.EN.Exchange.Entities.Appointment"/> items for the current user's Mailbox that start during 
        /// the given date range.
        /// </summary>
        /// <param name="start">Items that start on or after this date.</param>
        /// <param name="end">Items that start on or before this date.</param>
        /// <returns>A <see cref="System.Collections.Generic.IList&lt;T&gt;"/> of type 
        /// <see cref="MIS.EN.Exchange.Entities.Appointment"/>.</returns>
        public IList<Appointment> GetAppointments(DateTime start, DateTime end)
        {
            this.CheckUserMailBox();
            CompositeSpecification<Appointment> specification = new GreaterThanOrEqualToSpecification<Appointment, 
                DateTime>("StartDate", start).And(new LessThanOrEqualToSpecification<Appointment, DateTime>("StartDate", end));
            return this.calendarWorker.Retrieve(this.userMailBox, false, specification);
        }

        /// <summary>
        /// Gets all of the <see cref="MIS.EN.Exchange.Entities.Contact"/> items for the current user's Mailbox.
        /// </summary>
        /// <returns>A <see cref="System.Collections.Generic.IList&lt;T&gt;"/> of type <see cref="MIS.EN.Exchange.Entities.Contact"/>.</returns>
        public IList<Contact> GetContacts()
        {
            this.CheckUserMailBox();
            return this.contactWorker.Retrieve(this.userMailBox, false);
        }

        /// <summary>
        /// Gets all of the <see cref="MIS.EN.Exchange.Entities.Task"/> items for the current user's Mailbox.
        /// </summary>
        /// <returns>A <see cref="System.Collections.Generic.IList&lt;T&gt;"/> of type <see cref="MIS.EN.Exchange.Entities.Task"/>.</returns>
        public IList<Task> GetTasks()
        {
            this.CheckUserMailBox();
            return this.taskWorker.Retrieve(this.userMailBox, false);
        }

        private void ValidateCredentials()
        {
            if (this.credentials == null)
            {
                throw new ArgumentNullException("credentials");
            }
        }

        private void ValidateUserMailBox()
        {
            this.userMailBoxValidated = (this.userMailBox != null);
        }

        private void CheckUserMailBox()
        {
            if (!this.userMailBoxValidated)
            {
                throw new ApplicationException("Must pass in a valid user mailbox.");
            }
        }
    }
}
